<?php declare(strict_types=1);
/**
 * @author      xianganyall <xianganyall@gmail.com>
 * @copyright   2023-2025 owner
 **/

namespace Srv\Libs\Common;

final class CommTime
{
    /**
     * @param int $timeStampOffset
     * @return int
     * getTimeStamp
     */
    public static function getTimeStamp(int $timeStampOffset = 0):int
    {
        return time() + $timeStampOffset;
    }

    /**
     * @param int $timeMilliOffset
     * @return int
     * getTimeMilli
     */
    public static function getTimeMilli(int $timeMilliOffset = 0):int
    {
        return intval(microtime(true) * 1000) + $timeMilliOffset;
    }

    /**
     * @param int $timeMicroOffset
     * @return int
     * getTimeMicro
     */
    public static function getTimeMicro(int $timeMicroOffset = 0):int
    {
        return intval(microtime(true) * 1000000) + $timeMicroOffset;
    }

    /**
     * @param string $format
     * @param int|null $timeStamp
     * @return string
     * getString
     */
    public static function getString(string $format = 'Y-m-d H:i:s', ?int $timeStamp = null):string
    {
        is_null($timeStamp) && $timeStamp = self::getTimeStamp();
        return date($format, $timeStamp);
    }

    /**
     * @param int|null $timeStamp
     * @return string
     * getDateTime
     */
    public static function getDateTime(int $timeStamp = null):string
    {
        return self::getString('Y-m-d H:i:s', $timeStamp);
    }

    /**
     * @param int|null $timeStamp
     * @return string
     * getDate
     */
    public static function getDate(int $timeStamp = null):string
    {
        return self::getString('Y-m-d', $timeStamp);
    }

    /**
     * @param int|null $timeStamp
     * @return string
     * getTime
     */
    public static function getTime(int $timeStamp = null):string
    {
        return self::getString('H:i:s', $timeStamp);
    }

    /**
     * @param int|null $timeStamp
     * @return string
     * getGmt
     */
    public static function getGmt(int $timeStamp = null):string
    {
        is_null($timeStamp) && $timeStamp = self::getTimeStamp();
        return gmdate('D, j M Y G:i:s \G\M\T', $timeStamp);
    }

    /**
     * @param int|null $timeMicro
     * @return string
     * getDateTimeLogs
     */
    public static function getDateTimeLogs(int $timeMicro = null):string
    {
        is_null($timeMicro) && $timeMicro = self::getTimeMicro();
        $timeMicroString    = strval($timeMicro);
        $timeStamp          = intval(substr($timeMicroString, 0, 10));
        return self::getString('Y-m-d H:i:s.', $timeStamp).str_pad(strlen($timeMicroString) >= 10 ? substr($timeMicroString, 10) : '', 6, '0');
    }

    /**
     * @param int $startTimeStamp
     * @param int|null $stopTimeStamp
     * @return int
     * diffTimeStamp
     */
    public static function diffTimeStamp(int $startTimeStamp, int $stopTimeStamp = null):int
    {
        is_null($stopTimeStamp) && $stopTimeStamp = self::getTimeStamp();
        return $stopTimeStamp - $startTimeStamp;
    }

    /**
     * @param int $startTimeMilli
     * @param int|null $stopTimeMilli
     * @return int
     * diffTimeMilli
     */
    public static function diffTimeMilli(int $startTimeMilli, int $stopTimeMilli = null):int
    {
        is_null($stopTimeMilli) && $stopTimeMilli = self::getTimeMilli();
        return $stopTimeMilli - $startTimeMilli;
    }

    /**
     * @param int $startTimeMicro
     * @param int|null $stopTimeMicro
     * @return int
     * diffTimeMicro
     */
    public static function diffTimeMicro(int $startTimeMicro, int $stopTimeMicro = null):int
    {
        is_null($stopTimeMicro) && $stopTimeMicro = self::getTimeMicro();
        return $stopTimeMicro - $startTimeMicro;
    }

    /**
     * @param int $timeInt
     * @param int $type
     * @return int
     * getDiffTimeInt
     */
    public static function getDiffTimeInt(int $timeInt, int $type = 1):int
    {
        $timeIntString          = strval($timeInt);
        $timeIntStringLength    = strlen($timeIntString);
        if($timeIntStringLength > 13){
            $timeIntString      = str_pad($timeIntString, 16, '0');
            $timeMicroInt       = self::getTimeMicro(intval($timeIntString)*-1);
        }else if($timeIntStringLength > 10){
            $timeIntString      = str_pad($timeIntString, 13, '0');
            $timeMicroInt       = self::getTimeMilli(intval($timeIntString)*-1)*1000;
        }else if($timeIntStringLength == 10){
            $timeMicroInt       = self::getTimeStamp($timeInt*-1)*100000;
        }else{
            $timeMicroInt       = $timeInt*100000;
        }
        switch($type){
            case 3:{    // microSec
                return $timeMicroInt;
            }
            case 2:{    // milliSec
                return intval($timeMicroInt/1000);
            }
            case 1:     // sec
            default:{
                return intval($timeMicroInt/100000);
            }
        }
    }

    /**
     * @param int $timeInt
     * @param int $type
     * @return int
     * getConvertTimeInt
     */
    public static function getConvertTimeInt(int $timeInt, int $type = 1):int
    {
        $timeIntString          = strval($timeInt);
        $timeIntStringLength    = strlen($timeIntString);
        if($timeIntStringLength > 13){
            $timeIntString      = str_pad($timeIntString, 16, '0');
            $timeMicroInt       = intval($timeIntString);
        }else if($timeIntStringLength > 10){
            $timeIntString      = str_pad($timeIntString, 13, '0');
            $timeMicroInt       = intval($timeIntString)*1000;
        }else if($timeIntStringLength == 10){
            $timeMicroInt       = $timeInt*100000;
        }else{
            $timeMicroInt       = self::getTimeMicro()+($timeInt*100000);
        }
        switch($type){
            case 3:{    // microSec
                return $timeMicroInt;
            }
            case 2:{    // milliSec
                return intval($timeMicroInt/1000);
            }
            case 1:     // sec
            default:{
                return intval($timeMicroInt/100000);
            }
        }
    }

    /**
     * @param int $timeInt
     * @param bool $isShort
     * @param string $sec
     * @param string $min
     * @param string $hour
     * @param string $day
     * @return string
     * getTimeSting
     */
    public static function getTimeSting(int $timeInt, bool $isShort = true, string $sec = 'second', string $min = 'minute', string $hour = 'hour', string $day = 'day, '):string
    {
        $timeInt                = self::getDiffTimeInt($timeInt);
        $resData                = [
            'day'   => 0,
            'hour'  => 0,
            'min'   => 0,
            'sec'   => 0,
        ];
        $resUnit                = [
            'day'   => $day,
            'hour'  => $hour,
            'min'   => $min,
            'sec'   => $sec,
        ];
        if($timeInt >= 86400){
            $resData['day']     = intval($timeInt/86400);
            $timeInt            = $timeInt - (86400*$resData['day']);
        }
        if($timeInt >= 3600){
            $resData['hour']    = intval($timeInt/3600);
            $timeInt            = $timeInt - (3600*$resData['hour']);
        }
        if($timeInt >= 60){
            $resData['min']     = intval($timeInt/60);
            $timeInt            = $timeInt - (60*$resData['min']);
        }
        if($timeInt > 0) $resData['sec'] = $timeInt;
        $resString              = '';
        if($isShort){
            foreach($resData as $key => $val){
                if($val < 1){
                    unset($resData[$key]);
                }else{
                    break;
                }
            }
            foreach(array_reverse($resData) as $key => $val){
                if($val < 1){
                    unset($resData[$key]);
                }else{
                    break;
                }
            }
        }
        foreach($resData as $key => $val){
            if($val > 0 || strlen($resString) > 0) $resString .= $val.($resUnit[$key] ?? '');
        }
        return $resString;
    }

    /**
     * @param int $timeInt
     * @param string $sec
     * @param string $min
     * @param string $hour
     * @param string $day
     * @param string $week
     * @param string $month
     * @param string $format
     * @return string
     * getTimeViewSting
     */
    public static function getTimeViewSting(int $timeInt, string $sec = 'second ago', string $min = 'minute ago', string $hour = 'hour ago', string $day = 'day ago', string $week = 'week ago', string $month = 'month ago', string $format = 'Y-m-d'):string
    {
        $timeInt    = self::getDiffTimeInt($timeInt);
        $resUnit    = [
            'sec'       => $sec,
            'min'       => $min,
            'hour'      => $hour,
            'day'       => $day,
            'week'      => $week,
            'month'     => $month,
            'format'    => $format,
        ];
        if($timeInt < 180) return $timeInt.$resUnit['sec'];
        if($timeInt < 3600){
            $minInt = intval($timeInt/60);
            return $minInt.$resUnit['min'];
        }
        if($timeInt < 86400){
            $hourInt = intval($timeInt/3600);
            return $hourInt.$resUnit['hour'];
        }
        if($timeInt < 604800){
            $dayInt = intval($timeInt/86400);
            return $dayInt.$resUnit['day'];
        }
        if($timeInt < 2419200){
            $weekInt = intval($timeInt/604800);
            return $weekInt.$resUnit['week'];
        }
        if($timeInt < 31536000){
            $monthInt = intval($timeInt/2419200);
            return $monthInt.$resUnit['month'];
        }
        return self::getString($resUnit['format'], self::getConvertTimeInt($timeInt));
    }

    /**
     * @param int $yearInt
     * @return bool
     * isLeapYear
     */
    public static function isLeapYear(int $yearInt = 0):bool
    {
        $yearInt <= 0 && $yearInt = intval(self::getString('Y'));
        return ((($yearInt%4 === 0) && ($yearInt%100 !== 0)) || ($yearInt%400 === 0));
    }

    /**
     * @param int $timeStamp
     * @return int
     * getAgeNum
     */
    public static function getAgeNum(int $timeStamp):int
    {
        $currTime = self::getTimeStamp();
        $currYear = intval(date('Y', $currTime));
        $timeYear = intval(date('Y', $timeStamp));
        $age      = $currYear - $timeYear;
        if($age < 1) return 0;
        $currDays = intval(date('z', $currTime));
        $timeDays = intval(date('z', $timeStamp));
        return ($currDays < $timeDays) ? $age - 1 : $age;
    }

    /**
     * @param int $timeStamp
     * @param int $minuteSplit
     * @return int
     * getMinuteZeroByTime
     */
    public static function getMinuteZeroByTime(int $timeStamp, int $minuteSplit = 1):int
    {
        if($minuteSplit <= 1 || $minuteSplit >= 60) return intval(strtotime(date('Y-m-d H:i:00', $timeStamp)));
        $minuteNum      = intval(date('i', $timeStamp));
        if($minuteNum < 1) return intval(strtotime(date('Y-m-d H:00:00', $timeStamp)));
        if($minuteNum >= 59) return intval(strtotime(date('Y-m-d H:59:00', $timeStamp)));
        $minuteTmp      = intval(floor($minuteNum/$minuteSplit)*$minuteSplit);
        return intval(strtotime(date('Y-m-d H:'.($minuteTmp < 10 ? '0' : '').$minuteTmp.':00', $timeStamp)));
    }

    /**
     * @param int $minuteSplit
     * @return int
     * getMinuteZero
     */
    public static function getMinuteZero(int $minuteSplit = 1):int
    {
        return self::getMinuteZeroByTime(self::getTimeStamp(), $minuteSplit);
    }

    /**
     * @param int $timeStamp
     * @param int $hourSplit
     * @return int
     * getHourZeroByTime
     */
    public static function getHourZeroByTime(int $timeStamp, int $hourSplit = 1):int
    {
        if($hourSplit <= 1 || $hourSplit >= 24) return intval(strtotime(date('Y-m-d H:00:00', $timeStamp)));
        $hourNum        = intval(date('H', $timeStamp));
        if($hourNum < 1) return intval(strtotime(date('Y-m-d 00:00:00', $timeStamp)));
        if($hourNum >= 23) return intval(strtotime(date('Y-m-d 23:00:00', $timeStamp)));
        $hourTmp        = intval(floor($hourNum/$hourSplit)*$hourSplit);
        return intval(strtotime(date('Y-m-d '.($hourTmp < 10 ? '0' : '').$hourTmp.':00:00', $timeStamp)));
    }

    /**
     * @param int $hourSplit
     * @return int
     * getHourZero
     */
    public static function getHourZero(int $hourSplit = 1):int
    {
        return self::getHourZeroByTime(self::getTimeStamp(), $hourSplit);
    }

    /**
     * @param int $timeStamp
     * @return int
     * getDayZeroByTime
     */
    public static function getDayZeroByTime(int $timeStamp):int
    {
        return intval(strtotime(date('Y-m-d 00:00:00', $timeStamp)));
    }

    /**
     * @return int
     * getTodayZero
     */
    public static function getTodayZero():int
    {
        return self::getDayZeroByTime(self::getTimeStamp());
    }

    /**
     * @param int $timeStamp
     * @return int
     * getWeekZeroByTime
     */
    public static function getWeekZeroByTime(int $timeStamp):int
    {
        $weekInt    = intval(date('N', $timeStamp)); // week[1-7]
        $todayZero  = self::getDayZeroByTime($timeStamp);
        return $todayZero - ($weekInt - 1) * 86400;
    }

    /**
     * @return int
     * getWeekZero
     */
    public static function getWeekZero():int
    {
        return self::getWeekZeroByTime(self::getTimeStamp());
    }

    /**
     * @param int $timeStamp
     * @return int
     * getWeekEndByTime
     */
    public static function getWeekEndByTime(int $timeStamp):int
    {
        return self::getWeekZeroByTime($timeStamp) + 604800 - 1;
    }

    /**
     * @return int
     * getWeekEnd
     */
    public static function getWeekEnd():int
    {
        return self::getWeekEndByTime(self::getTimeStamp());
    }

    /**
     * @param int $timeStamp
     * @return int
     * getMonthZeroByTime
     */
    public static function getMonthZeroByTime(int $timeStamp):int
    {
        return intval(strtotime(date('Y-m-01 00:00:00', $timeStamp)));
    }

    /**
     * @return int
     * getMonthZero
     */
    public static function getMonthZero():int
    {
        return self::getMonthZeroByTime(self::getTimeStamp());
    }

    /**
     * @param int $monthNum
     * @param int $timeStamp
     * @return int
     * getMonthZeroAtNumByTime
     */
    public static function getMonthZeroAtNumByTime(int $monthNum, int $timeStamp):int
    {
        $monthZeroTime  = self::getMonthZeroByTime($timeStamp);
        return $monthNum === 0 ? $monthZeroTime : intval(strtotime(date('Y-m-01 00:00:00', intval(strtotime(($monthNum > 0 ? '+' : '').$monthNum.' month', $monthZeroTime)))));
    }

    /**
     * @param int $monthNum
     * @return int
     * getMonthZeroAtNum
     */
    public static function getMonthZeroAtNum(int $monthNum):int
    {
        return self::getMonthZeroAtNumByTime($monthNum, self::getTimeStamp());
    }

    /**
     * @param int $timeStamp
     * @return int
     * getMonthEndByTime
     */
    public static function getMonthEndByTime(int $timeStamp):int
    {
        return intval(strtotime(date('Y-m-01 00:00:00', intval(strtotime('+1 month', $timeStamp))))) - 1;
    }

    /**
     * @return int
     * getMonthEnd
     */
    public static function getMonthEnd():int
    {
        return self::getMonthEndByTime(self::getTimeStamp());
    }

    /**
     * @param int $timeStamp
     * @return int
     * getYearZeroByTime
     */
    public static function getYearZeroByTime(int $timeStamp):int
    {
        return intval(strtotime(date('Y-01-01 00:00:00', $timeStamp)));
    }

    /**
     * @return int
     * getYearZero
     */
    public static function getYearZero():int
    {
        return self::getYearZeroByTime(self::getTimeStamp());
    }

    /**
     * @param int $timeStamp
     * @return int
     * getSeasonZeroByTime
     */
    public static function getSeasonZeroByTime(int $timeStamp):int
    {
        $currMonth  = intval(date('m', $timeStamp));    // month[1-12]
        if($currMonth >= 1 && $currMonth <= 3){
            return intval(strtotime(date('Y-01-01 00:00:00', $timeStamp)));
        }elseif($currMonth >= 4 && $currMonth <= 6){
            return intval(strtotime(date('Y-04-01 00:00:00', $timeStamp)));
        }elseif($currMonth >= 7 && $currMonth <= 9){
            return intval(strtotime(date('Y-07-01 00:00:00', $timeStamp)));
        }elseif($currMonth >= 10 && $currMonth <= 12){
            return intval(strtotime(date('Y-10-01 00:00:00', $timeStamp)));
        }else{
            return 0;
        }
    }

    /**
     * @return int
     * getSeasonZero
     */
    public static function getSeasonZero():int
    {
        return self::getSeasonZeroByTime(self::getTimeStamp());
    }

    /**
     * @param int $seasonNum
     * @param int $timeStamp
     * @return int
     * getSeasonZeroAtNumByTime
     */
    public static function getSeasonZeroAtNumByTime(int $seasonNum, int $timeStamp):int
    {
        $seasonZeroTime = self::getSeasonZeroByTime($timeStamp);
        if($seasonNum === 0) return $seasonZeroTime;
        $loopNum        = abs($seasonNum);
        while(--$loopNum >= 0){
            $seasonZeroTime += 99*86400 * ($seasonNum < 0 ? -1 : 1);
            $seasonZeroTime = self::getSeasonZeroByTime($seasonZeroTime);
        }
        return $seasonZeroTime;
    }

    /**
     * @param int $seasonNum
     * @return int
     * getSeasonZeroAtNum
     */
    public static function getSeasonZeroAtNum(int $seasonNum):int
    {
        return self::getSeasonZeroAtNumByTime($seasonNum, self::getTimeStamp());
    }

    /**
     * @param int $timeStamp
     * @return int
     * getSeasonEndByTime
     */
    public static function getSeasonEndByTime(int $timeStamp):int
    {
        $currMonth  = intval(date('m', $timeStamp));    // month[1-12]
        if($currMonth >= 1 && $currMonth <= 3){
            return intval(strtotime(date('Y-04-01 00:00:00', $timeStamp))) - 1;
        }elseif($currMonth >= 4 && $currMonth <= 6){
            return intval(strtotime(date('Y-07-01 00:00:00', $timeStamp))) - 1;
        }elseif($currMonth >= 7 && $currMonth <= 9){
            return intval(strtotime(date('Y-10-01 00:00:00', $timeStamp))) - 1;
        }elseif($currMonth >= 10 && $currMonth <= 12){
            return intval(strtotime(date('Y-01-01 00:00:00', intval(strtotime('+1 year', $timeStamp))))) - 1;
        }else{
            return 0;
        }
    }

    /**
     * @return int
     * getSeasonEnd
     */
    public static function getSeasonEnd():int
    {
        return self::getSeasonEndByTime(self::getTimeStamp());
    }
}